package fr.emse.tatiana.scoresheetvisualisation;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.eclipse.core.runtime.Path;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import fr.emse.tatiana.graphical.DisplayRule;
import fr.emse.tatiana.graphical.DisplayRulesParser;
import fr.emse.tatiana.replayable.AbstractReplayableEditor;
import fr.emse.tatiana.replayable.IGraphicalEditor;
import fr.emse.tatiana.replayable.IReplayableModel;
import fr.emse.tatiana.replayable.IReplayableSaver;
import fr.emse.tatiana.replayable.VisualisationReplayableInput;
import fr.emse.tatiana.replayable.analysis.IAnalysis;

public class ScoreSheetDisplayEditor extends AbstractReplayableEditor implements IGraphicalEditor {


	DisplayRulesParser saver;


	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.emse.tatiana.replayable.AbstractReplayableEditor#addAnalysis(fr.emse.tatiana.replayable.analysis.Analysis)
	 */
	@Override
	public void addAnalysis(IAnalysis a) {
		super.addAnalysis(a);
		if (model != null) {
			Object[] rows = model.getAnalysedEvents(a);
			updateViewer(rows);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.emse.tatiana.replayable.AbstractReplayableEditor#removeAnalysis(fr.emse.tatiana.replayable.analysis.Analysis)
	 */
	@Override
	public void removeAnalysis(IAnalysis a) {
		super.removeAnalysis(a);
		rulesviewer.refresh();
		facetviewer.refresh();
		gde.refresh();
	}

	public static String ID = "fr.emse.tatiana.ScoreSheetDisplayEditor";
	private VisualisationReplayableInput input;

	private IReplayableModel model;

	public ScoreSheetDisplayEditor() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.emse.tatiana.replayable.AbstractReplayableEditor#init(org.eclipse.ui.IEditorSite,
	 *      org.eclipse.ui.IEditorInput)
	 */
	@Override
	public void init(IEditorSite site, IEditorInput input)
	throws PartInitException {
		super.init(site, input);

		if (input instanceof VisualisationReplayableInput) {
			this.input = (VisualisationReplayableInput) input;
			model = this.input.getDisplayModel(analyses);
			saver = new DisplayRulesParser(this.input.getFile().getParent().getFile(new Path("."+this.input.getFile().getName()+".scorelayout")),
					model);
			this.rules = saver.getRules();
		} else {
			throw new PartInitException("Bad input for Display");
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.emse.tatiana.replayable.AbstractReplayableEditor#isSaveAsAllowed()
	 */
	@Override
	public boolean isSaveAsAllowed() {
		return false; // don't allow saving as
	}

	@Override
	public IReplayableModel getModel() {
		return model;
	}

	@Override
	public IReplayableSaver getReplayableSaver() {
		return saver;
	}

	@Override
	protected void selectEvents(final StructuredSelection selection,
			final boolean scroll, final boolean propagate) {
		final ScoreSheetDisplayEditor self = this;
		PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
			public void run() {
				try {
					if (!propagate)
						viewer.removeSelectionChangedListener(self);
					gde.setSelection(selection);
				} finally {
					if (!propagate)
						viewer.addSelectionChangedListener(self);
				}
			}
		});

	}

	@Override
	protected void startSynchronisation() {
		viewer.addSelectionChangedListener(this);

	}

	@Override
	protected void endSynchronisation() {
		viewer.removeSelectionChangedListener(this);

	}

	@Override
	protected void updateTitle(int facet, String name) {
		facetviewer.refresh();
		rulesviewer.refresh();
	}

	@Override
	protected void updateViewer(Object[] rows) {
		gde.refresh();
		facetviewer.refresh();
		rulesviewer.refresh();
	}

	@Override
	protected void createPages() {
		createGraphicalViewer();
		createRulesEditor();
	}

	@Override
	protected Object getEventFromSelection(Object o) {
		return ((EventPart) o).getModel();
	}

	List<DisplayRule> rules;
	private GraphicalViewer viewer;
	private GraphicalDisplayEditor gde;

	protected void createGraphicalViewer() {

		try {
			gde = new GraphicalDisplayEditor(model, rules);
			int index = addPage(gde, input);
			setPageText(index, "Replayable");
			viewer = gde.getViewer();
		} catch (PartInitException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private TableViewer rulesviewer;
	private String[] rulecolumns = new String[] { "Facet", "Facet Value",
			"Property", "Property Value" };
	private Button delete;
	private Button create;
	private Button edit;
	private DisplayRule currentrule;
	protected String facetname;
	protected String facetvalue;
	protected String property;
	protected String propertyvalue;
	protected ComboViewer facetvalueviewer;
	protected ComboViewer facetviewer;

	protected void createRulesEditor() {
		Composite c = new Composite(getContainer(), SWT.NONE);

		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 4;
		gridLayout.verticalSpacing = 10;
		gridLayout.marginHeight = 0;
		gridLayout.marginWidth = 0;
		c.setLayout(gridLayout);

		rulesviewer = new TableViewer(c, SWT.H_SCROLL | SWT.V_SCROLL
				| SWT.BORDER | SWT.FULL_SELECTION);
		Table table = rulesviewer.getTable();
		GridData griddata = new GridData(GridData.FILL, GridData.FILL, true,
				true);
		griddata.horizontalSpan = 4;
		table.setLayoutData(griddata);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		for (int i = 0; i < rulecolumns.length; i++) {
			TableColumn column = new TableColumn(table, SWT.LEFT);
			column.setText(rulecolumns[i]);
			column.pack();
		}
		rulesviewer.setColumnProperties(rulecolumns);

		rulesviewer.setContentProvider(new IStructuredContentProvider() {

			public Object[] getElements(Object inputElement) {
				return rules.toArray();
			}

			public void dispose() {

			}

			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {
				// pretend we never change the input;
			}

		});
		rulesviewer.setLabelProvider(new RulesLabelProvider());

		rulesviewer
		.addSelectionChangedListener(new ISelectionChangedListener() {

			public void selectionChanged(SelectionChangedEvent event) {
				StructuredSelection sel = (StructuredSelection) rulesviewer
				.getSelection();
				currentrule = (sel.isEmpty()) ? null
						: (DisplayRule) sel.getFirstElement();
				delete.setEnabled(!sel.isEmpty());
				edit.setEnabled(!sel.isEmpty());
			}

		});

		rulesviewer.setInput(rules);

		new Label(c, SWT.NONE).setText(rulecolumns[0]);
		new Label(c, SWT.NONE).setText(rulecolumns[1]);
		new Label(c, SWT.NONE).setText(rulecolumns[2]);
		new Label(c, SWT.NONE).setText(rulecolumns[3]);

		facetviewer = new ComboViewer(c);
		facetviewer.setContentProvider(new IStructuredContentProvider() {

			public Object[] getElements(Object inputElement) {
				if (inputElement == null) {
					return new Object[] {};
				}
				IReplayableModel model = (IReplayableModel) inputElement;
				List<String> ret = new Vector<String>();
				ret.add("Any");
				for (int i = 0; i < model.getFacetCount(); i++) {
					ret.add(model.getFacetName(i));
				}
				return ret.toArray();
			}

			public void dispose() {
			}

			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {
			}

		});

		facetviewer
		.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				String elem = (String) ((IStructuredSelection) event
						.getSelection()).getFirstElement();
				if ("Any".equals(elem)) {
					facetname = null;
				} else {
					facetname = elem;
				}
				facetvalueviewer.refresh();
			}
		});

		facetviewer.setInput(model);

		facetvalueviewer = new ComboViewer(c);

		facetvalueviewer.setContentProvider(new IStructuredContentProvider() {

			public Object[] getElements(Object inputElement) {
				if (inputElement == null) {
					return new Object[] {};
				}
				IReplayableModel model = (IReplayableModel) inputElement;
				Set<String> ret = new HashSet<String>();
				for (int e = 0; e < model.getEventCount(); e++) {
					Object event = model.getEvent(e);
					if (facetname == null) {
						for (int i = 0; i < model.getFacetCount(); i++) {
							String s = model.getFacetText(event, i);
							ret.add((s != null && s.length() > 50) ? s
									.substring(0, 50)
									+ "..." : s);
						} // TODO don't do this here, do it in a label provider
					} else {
						String s = model.getFacetText(event, model.getFacetCol(facetname));
						ret.add((s != null && s.length() > 50) ? s.substring(0,
								50)
								+ "..." : s);
					}
				}
				List<String> l = new LinkedList<String>(ret);
				if (l.contains(null)) {
					l.remove(null);
				}
				java.util.Collections.sort(l);
				l.add(0, "Any");
				return l.toArray();
			}

			public void dispose() {
			}

			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {
			}

		});

		facetvalueviewer
		.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				String elem = (String) ((IStructuredSelection) event
						.getSelection()).getFirstElement();
				if ("Any".equals(elem)) {
					facetvalue = null;
				} else {
					facetvalue = elem;
				}
			}
		});

		facetvalueviewer.setInput(model);

		final ComboViewer propertyviewer = new ComboViewer(c);
		propertyviewer.setContentProvider(new IStructuredContentProvider() {

			public Object[] getElements(Object inputElement) {
				return DisplayRule.properties;
			}

			public void dispose() {
			}

			public void inputChanged(Viewer viewer, Object oldInput,
					Object newInput) {
			}

		});

		propertyviewer
		.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				String elem = (String) ((IStructuredSelection) event
						.getSelection()).getFirstElement();
				property = elem;
			}
		});

		propertyviewer.setInput(new Object());

		final Text propertyvaluew = new Text(c, SWT.BORDER);

		propertyvaluew.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				propertyvalue = ((Text) e.getSource()).getText();

			}
		});

		create = new Button(c, SWT.PUSH);
		create.setText("Create Rule");
		create.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				// doesn't happen
			}

			public void widgetSelected(SelectionEvent e) {
				createRule();
			}
		});

		delete = new Button(c, SWT.PUSH);
		delete.setText("Delete Selected Rule");
		delete.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				// doesn't happen
			}

			public void widgetSelected(SelectionEvent e) {
				deleteRule();
			}
		});
		delete.setEnabled(false);
		
		
		
		edit = new Button(c,SWT.PUSH);
		edit.setText("Edit Selected Rule");
		edit.addSelectionListener(new SelectionListener(){

			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO Auto-generated method stub
				
			}

			public void widgetSelected(SelectionEvent e) {
				// TODO Auto-generated method stub	
				EditRuleDialog dialog=new EditRuleDialog(PlatformUI.getWorkbench().getDisplay().getActiveShell(),model,currentrule.getFacet(),currentrule.getFacetvalue(),currentrule.getProperty(),currentrule.getPropertyvalue());
				
				if(!(dialog.open()==0)){
					return;
				}
				if (rules.contains(currentrule))
					rules.remove(currentrule);
				rules.add(new DisplayRule(dialog.facetname,dialog.facetvalue, dialog.property, dialog.propertyvalue));
				touchRules();
				rulesviewer.refresh();
				gde.refresh();
			}
			
		});
		edit.setEnabled(false);
		

		int index = addPage(c);
		setPageText(index, "Visualisation Rules");
	}

	protected void deleteRule() {
		if (rules.contains(currentrule))
			rules.remove(currentrule);
		touchRules();
		rulesviewer.refresh();
		gde.refresh();
	}

	protected void createRule() {
		rules.add(new DisplayRule(facetname, facetvalue, property, propertyvalue));
		touchRules();
		rulesviewer.refresh();
		gde.refresh();
	}

	private class RulesLabelProvider extends LabelProvider implements
	ITableLabelProvider {

		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		public String getColumnText(Object element, int columnIndex) {
			DisplayRule rule = (DisplayRule) element;
			switch (columnIndex) {
			case 0:
				try {
					return (rule.getFacet() == null) ? "Any" : rule.getFacet();
				} catch (Exception e) {
					return "facet "+rule.getFacet(); // probably not needed
				}
			case 1:
				return (rule.getFacetvalue() == null) ? "Any" : rule
						.getFacetvalue();
			case 2:
				return (rule.getProperty() == null) ? "Any" : rule
						.getProperty();
			case 3:
			default:
				return (rule.getPropertyvalue() == null) ? "$"
						+ getColumnText(element, 0) : rule.getPropertyvalue()
						.toString();
			}
		}

	}

	protected void touchRules() {
		saver.touch();
		firePropertyChange(PROP_DIRTY);
	}

	public GraphicalViewer getViewer() {
		return viewer;
	}
	

}
